package org.opens.lambda.future;

import org.junit.Test;

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;


public class FutureAction {

    /**
     * 功能:
     *      自定义一个Future, Callable实现异步阻塞.
     * 输出:
     *      null
     *      //中间这里如果Future线程没有执行完毕则主线程会一直等待.
     *      i'm finished
     * 说明:
     *      1. 这个程序的输出说明了异步回调机制的简单实现.
     *          - 其实就是在一个很耗时的操作没有执行完毕时就可以先返回为完成时的状态, 把这个任务交个其他线程去做, 不需要使
     *          主线程阻塞.
     */
    @Test
    public void test1() throws InterruptedException {
        Future<String> future = invoke(() -> {
            try {
                //这个线程睡眠用来模拟长时间操作造成的线程阻塞.
                Thread.sleep(10000);
                return "i'm finished";
            } catch (InterruptedException e) {
                e.printStackTrace();
                return "error";
            }
        });
        System.out.println(future.get());
        while(!future.isDone()) {
            Thread.sleep(10000);
        }
        System.out.println(future.get());

    }

    /**
     * 功能:
     *      传统单线程阻塞式.
     * 输出:
     *      //带带10秒钟
     *      i'm finished
     * 说明:
     *      1. 其实就是上面的代码陷入阻塞, 下面的代码就得等待.
     */
    @Test
    public void test2() {
        String block = block(() -> {
            try {
                //这个线程睡眠用来模拟长时间操作造成的线程阻塞.
                Thread.sleep(10000);
                return "i'm finished";
            } catch (InterruptedException e) {
                e.printStackTrace();
                return "error";
            }
        });
        System.out.println(block);
    }

    private static <T> T block(Callable<T> callable) {
        return callable.action();
    }

    private static <T> Future<T> invoke(Callable<T> callable) {

        //原子操作类, 可以实现安全的保存操作
        AtomicReference<T> result = new AtomicReference<>();
        AtomicBoolean finished = new AtomicBoolean(false);
        Thread thread = new Thread(() -> {
            T value = callable.action();
            result.set(value);
            finished.set(true);
        });

        Future<T> future = new Future<T>() {
            @Override
            public T get() {
                return result.get();
            }

            @Override
            public boolean isDone() {
                return finished.get();
            }
        };

        thread.start();
        return future;
    }

    private interface Future<T> {

        T get();

        boolean isDone();

    }

    private interface Callable<T> {
        T action();
    }

}
